package com.bootx.service.predict;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 方法三：趋势外推法（线性回归）
 * 新增：输出1到20个位置红包的奇偶性，开包时间，奇数概率
 */
public class LinearTrendPredictor {

    public static List<Prediction> predict(List<RedPackageRound> rounds) {
        long nextRound = rounds.getLast().roundId + 1L;

        Map<Integer, List<Long>> xs = new HashMap<>();
        Map<Integer, List<Double>> ysAmount = new HashMap<>();
        Map<Integer, List<Integer>> ysTime = new HashMap<>();
        Map<Integer, List<Integer>> ysOdd = new HashMap<>(); // 记录奇偶性，奇数=1，偶数=0

        for (RedPackageRound round : rounds) {
            for (RedPackageItem item : round.items) {
                int order = item.order;
                xs.computeIfAbsent(order, k -> new ArrayList<>()).add(round.roundId);

                int cents = getAmountLastTwoDigits(item.amount);
                ysAmount.computeIfAbsent(order, k -> new ArrayList<>()).add((double)cents);

                ysTime.computeIfAbsent(order, k -> new ArrayList<>()).add(item.openTime);

                int sumLastTwoDigits = getSumLastTwoDigits(item.amount);
                int odd = (sumLastTwoDigits % 2 == 1) ? 1 : 0;
                ysOdd.computeIfAbsent(order, k -> new ArrayList<>()).add(odd);
            }
        }

        List<Prediction> predictions = new ArrayList<>();
        for (int order = 1; order <= 20; order++) {
            List<Long> xList = xs.getOrDefault(order, new ArrayList<>());
            List<Double> amountList = ysAmount.getOrDefault(order, new ArrayList<>());
            List<Integer> timeList = ysTime.getOrDefault(order, new ArrayList<>());
            List<Integer> oddList = ysOdd.getOrDefault(order, new ArrayList<>());

            double[] amountFit = fitLine(xList, amountList);
            double[] timeFit = fitLineLongInt(xList, timeList);
            double[] oddFit = fitLineInt(xList, oddList);

            double predictedAmount = amountFit[0] * nextRound + amountFit[1];
            int predictedTime = (int) (timeFit[0] * nextRound + timeFit[1]);
            double predictedOddProb = clamp(oddFit[0] * nextRound + oddFit[1], 0.0, 1.0);

            boolean isOdd = predictedOddProb >= 0.5;

            predictions.add(new Prediction(order, predictedAmount, predictedTime, isOdd, predictedOddProb));
        }

        return predictions;
    }

    // 计算金额小数点后两位之和，例如 1.23 -> 2+3=5
    private static int getSumLastTwoDigits(double amount) {
        int cents = getAmountLastTwoDigits(amount);
        int tens = cents / 10;
        int ones = cents % 10;
        return tens + ones;
    }

    // 线性拟合，x是Long，y是Double，返回 [a, b]
    private static double[] fitLine(List<Long> xList, List<Double> yList) {
        int n = xList.size();
        if (n == 0) return new double[]{0, 0};

        double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
        for (int i = 0; i < n; i++) {
            double x = xList.get(i);
            double y = yList.get(i);
            sumX += x;
            sumY += y;
            sumXY += x * y;
            sumX2 += x * x;
        }
        double denominator = n * sumX2 - sumX * sumX;
        if (Math.abs(denominator) < 1e-6) return new double[]{0, sumY / n};

        double a = (n * sumXY - sumX * sumY) / denominator;
        double b = (sumY - a * sumX) / n;
        return new double[]{a, b};
    }

    // 线性拟合，x是Long，y是Integer，返回 [a, b]
    private static double[] fitLineLongInt(List<Long> xList, List<Integer> yList) {
        List<Double> yDouble = new ArrayList<>();
        for (Integer v : yList) {
            yDouble.add(v.doubleValue());
        }
        return fitLine(xList, yDouble);
    }

    // 线性拟合，x是Long，y是Integer，用于奇数概率拟合，返回 [a,b]
    private static double[] fitLineInt(List<Long> xList, List<Integer> yList) {
        return fitLineLongInt(xList, yList);
    }

    private static double clamp(double val, double min, double max) {
        return val < min ? min : (val > max ? max : val);
    }

    // 新增辅助方法
    private static int getAmountLastTwoDigits(double amount) {
        int cents = (int) Math.round((amount - Math.floor(amount)) * 100);
        return cents;  // 0~99
    }

    public static class Prediction {
        public int order;
        public double predictedAmount;
        public int predictedTime;
        public boolean isOdd;
        public double oddProbability;

        public Prediction(int order, double predictedAmount, int predictedTime, boolean isOdd, double oddProbability) {
            this.order = order;
            this.predictedAmount = predictedAmount;
            this.predictedTime = predictedTime;
            this.isOdd = isOdd;
            this.oddProbability = oddProbability;
        }

        @Override
        public String toString() {
            return String.format("Order %d: Amount=%.2f, Time=%d, IsOdd=%s, OddProb=%.3f",
                    order, predictedAmount, predictedTime, isOdd, oddProbability);
        }
    }
}
